package config

import (
	"fmt"
	"github.com/spf13/viper"
	_ "github.com/spf13/viper"
	"gorm.io/driver/mysql"
	"gorm.io/driver/postgres"
	"gorm.io/driver/sqlite"
	"gorm.io/driver/sqlserver"
	"gorm.io/gorm"
	"log"
	"strings"
)

const (
	DriverMySQL      = "mysql"
	DriverSQLite     = "sqlite"
	DriverPostgreSQL = "postgres"
	DriverSQLServer  = "sqlserver"
)

var cfg *Config

type Driver string

type MySQLConfig struct {
	Port     int    `yaml:"port" json:"port"`
	Host     string `yaml:"host" json:"host"`
	User     string `yaml:"user" json:"user"`
	Password string `yaml:"password" json:"password"`
	Database string `yaml:"database" json:"database"`
}

func (c MySQLConfig) Dsn() string {
	return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		c.User, c.Password, c.Host, c.Port, c.Database)
}

type PostgreSQLConfig struct {
	Host     string `yaml:"host" json:"host"`
	Port     int    `yaml:"port" json:"port"`
	User     string `yaml:"user" json:"user"`
	Password string `yaml:"password" json:"password"`
	Database string `yaml:"database" json:"database"`
}

func (p PostgreSQLConfig) Dsn() string {
	return fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=UTC",
		p.Host, p.User, p.Password, p.Database, p.Port)
}

type SQLServerConfig struct {
	Host     string `yaml:"host" json:"host"`
	Port     int    `yaml:"port" json:"port"`
	User     string `yaml:"user" json:"user"`
	Password string `yaml:"password" json:"password"`
	Database string `yaml:"database" json:"database"`
}

func (s SQLServerConfig) Dsn() string {
	return fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s",
		s.User, s.Password, s.Host, s.Port, s.Database)
}

type SQLiteConfig struct {
	Path string `yaml:"path" json:"path"`
}

func (s SQLiteConfig) Dsn() string {
	return s.Path + "?_journal_mode=WAL&_foreign_keys=1" // SQLite connection with Write-Ahead Logging
}

type DatabaseConfig struct {
	Driver     Driver           `yaml:"driver" json:"driver"`
	MySQL      MySQLConfig      `yaml:"mysql" json:"mysql"`
	SQLite     SQLiteConfig     `yaml:"sqlite" json:"sqlite"`
	PostgreSQL PostgreSQLConfig `yaml:"postgresql" json:"postgresql"`
	SQLServer  SQLServerConfig  `yaml:"sqlserver" json:"sqlserver"`
}

func (dc DatabaseConfig) GetDialector() gorm.Dialector {
	d := dc.Driver
	if d == DriverSQLite {
		return sqlite.Open(dc.SQLite.Dsn())
	}

	if d == DriverMySQL {
		return mysql.Open(dc.MySQL.Dsn())
	}

	if d == DriverPostgreSQL {
		return postgres.Open(dc.PostgreSQL.Dsn())
	}

	if d == DriverSQLServer {
		return sqlserver.Open(dc.SQLServer.Dsn())
	}

	return mysql.Open(dc.SQLServer.Dsn())
}

type WebAppConfig struct {
	Port     int `yaml:"port" json:"port"`
	Database DatabaseConfig
}

type Config struct {
	Web WebAppConfig `yaml:"web" json:"web"`
}

func Init() error {
	v := viper.GetViper()
	v.SetConfigName("config")
	v.SetConfigType("yaml")
	v.AutomaticEnv()
	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
	v.AddConfigPath(".")
	if err := v.ReadInConfig(); err != nil {
		log.Printf("failed to read the config:%s\n", err.Error())
		return err
	}
	cfg = &Config{}
	if err := v.Unmarshal(cfg); err != nil {
		return err
	}
	return nil
}

func GetConfig() *Config {
	return cfg
}
